package slevnik.Daemon;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import slevnik.CategoryManager;
import slevnik.CategoryManagerImpl;
import slevnik.Item;
import slevnik.ItemManager;
import slevnik.ItemManagerImpl;
import slevnik.SlevnikException;

/**
 * Class contains methods used for updating database with new coupons from coupon servers. 
 * 
 * @author Michal Halaj
 */
public class Daemon
{
    private List<XMLFeedProperties> websitesProperties;
    
    ItemManager itemManager = null;
    CategoryManager categoryManager = null;
    
    /*
     * Constructor.
     */

    public Daemon()
    {
        DataSource ds = new DataSource() {

            public Connection getConnection() throws SQLException {
                return getConnection("slevnik", "1fdfd7");
            }

            public Connection getConnection(String username, String password) throws SQLException {
                return DriverManager
                        .getConnection("jdbc:mysql://db4free.net:3306/", username, password);
            }

            public PrintWriter getLogWriter() throws SQLException {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            public void setLogWriter(PrintWriter out) throws SQLException {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            public void setLoginTimeout(int seconds) throws SQLException {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            public int getLoginTimeout() throws SQLException {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            public <T> T unwrap(Class<T> iface) throws SQLException {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            public boolean isWrapperFor(Class<?> iface) throws SQLException {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        };
        
        try {
            itemManager = new ItemManagerImpl(ds);
            categoryManager = new CategoryManagerImpl(ds);
        } catch (SQLException ex) {
            Logger.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
        }
                
        websitesProperties = new ArrayList<XMLFeedProperties>();

    }
    
    /*
     * Method loads properties and URL addresses from XML file.
     * Properties are used for searching data in XML feeds. 
     * URL addresses are links to the XML feeds.
     * 
     * @param xmlFileName name of xml file, which contains data parsed by this method
     */
    
    public void loadXMLFeedProperties(String xmlFileName)
    {
        websitesProperties = new ArrayList<XMLFeedProperties>();
        
        try 
        {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            
            Document doc = builder.parse(xmlFileName);

            XPathFactory xpf = XPathFactory.newInstance();
            XPath xPath = xpf.newXPath();

            NodeList xmlFeedPropertyElements = (NodeList) xPath.evaluate("//xml-property", doc, XPathConstants.NODESET);
            
            for (int i = 0; i < xmlFeedPropertyElements.getLength(); i++)
            {
                Element xmlFeedPropertyElement = (Element) xmlFeedPropertyElements.item(i);
                XMLFeedProperties xmlFeedProperty = new XMLFeedProperties();
                
                NodeList urlElements = (NodeList) xPath.evaluate("//url", xmlFeedPropertyElement, XPathConstants.NODESET);
                
                for (int j = 0; j < urlElements.getLength(); j++)
                {
                    Element urlElement = (Element) urlElements.item(j);
                    xmlFeedProperty.addUrl(urlElement.getTextContent());
                }               
                
                xmlFeedProperty.setDealXPath(xPath.evaluate("dealXPath", xmlFeedPropertyElement));                
                xmlFeedProperty.setTitleXPath(xPath.evaluate("titleXPath", xmlFeedPropertyElement));
                xmlFeedProperty.setUrlXPath(xPath.evaluate("urlXPath", xmlFeedPropertyElement));
                xmlFeedProperty.setCityXPath(xPath.evaluate("cityXPath", xmlFeedPropertyElement));
                xmlFeedProperty.setOriginalPriceXPath(xPath.evaluate("originalPriceXpath", xmlFeedPropertyElement));
                xmlFeedProperty.setFinalPriceXPath(xPath.evaluate("finalPriceXPath", xmlFeedPropertyElement));
                xmlFeedProperty.setDiscountXPath(xPath.evaluate("discount", xmlFeedPropertyElement));
                xmlFeedProperty.setDateFormat(xPath.evaluate("dateFormatXPath", xmlFeedPropertyElement));
                xmlFeedProperty.setDealStartXPath(xPath.evaluate("dealStartXPath", xmlFeedPropertyElement));
                xmlFeedProperty.setDealEndXPath(xPath.evaluate("dealEndXPath", xmlFeedPropertyElement));
                xmlFeedProperty.setCustomersXPath(xPath.evaluate("customersCountXPath", xmlFeedPropertyElement));
                xmlFeedProperty.setCategoryXPath(xPath.evaluate("categoryXPath", xmlFeedPropertyElement));   
                
                websitesProperties.add(xmlFeedProperty);
            }
            
        } catch (XPathExpressionException ex) {
            Logger.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SAXException ex) {
            Logger.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /*
     * Method updates database with new items parsed from XML feeds.
     * First, method loadXMLFeedProperties(String fileName) should be call to load URLs and properties.
     * 
     */

    public void updateServer()
    {
        try 
        {
            
            if(itemManager!=null && websitesProperties!=null)
            {
            
                List<Item> items = new ArrayList<Item>();               
                
                //loads deals from XML feeds
                for(XMLFeedProperties website : websitesProperties)
                {
                    XMLParser.getNewItems(website, items);
                }

                itemManager.removeAllItems();       
                itemManager.addItems(items);
            }
             
        } catch (SlevnikException ex) {
            Logger.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
        }    
    }
    
    /*
     * Daemon is started by this method.
     * 
     * @param args input from command line. Input shoulds be path to XML document,
     * which contains properties and URL addresses for parsing.
     * 
     */
    
    public static void main(String [] args)
    {
        if(args!=null && args.length != 1)
        {
            System.out.println("You must run Daemon with argument <xmlFile>.xml.");
            System.out.println("<xmlFile>.xml contains properties and URL addresses used for parsing.");
            System.out.println("Structure of <xmlFile>.xml is described by XML schema \"xml-properties.xsd\".");
        }
        else
        {        
            Daemon daemon = new Daemon();
            daemon.loadXMLFeedProperties(args[0]);
            daemon.updateServer();
        }
    }
}
